package com.yixing.tech.common.utils;


import com.yixing.tech.common.base.ConditionsVO;
import com.yixing.tech.common.service.CommonService;
import com.yixing.tech.common.vo.s3.RoleVO;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author yixing tech
 * at 2021/12/20
 * describe：
 */
@Slf4j
public class DataUtil {
    public static ModelMapper modelMapper = new ModelMapper();

    static {
        modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
    }

    /**
     * 对象属性拷贝
     *
     * @param vo
     * @param c
     * @param <T>
     * @return
     */
    public static <T> T o2o(Object vo, Class<T> c) {
        if (vo == null) return null;
        T t = null;
        try {
            t = c.newInstance();
            modelMapper.map(vo, t);
            log.debug(t.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 对象属性拷贝
     *
     * @param vo
     * @param type
     * @param <T>
     * @return
     */
    public static <T> T o2o(Object vo, Type type) {
        if (vo == null) return null;
        try {
            return modelMapper.map(vo, type);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 对象深拷贝
     *
     * @param src
     * @param <T>
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static <T> T deepClone(T src) throws Exception{
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(src);
        objectOutputStream.close();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object obj = objectInputStream.readObject();
        objectInputStream.close();
        return (T) obj;
    }

    /**
     * java对象转map
     */
    public static Map<String, Object> objectToMap(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        Field[] superFields = obj.getClass().getSuperclass().getDeclaredFields();
        for (Field field : superFields) {
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }

    /**
     * 角色权限合并
     *
     * @param currRole
     * @param selfRole
     * @return
     */
    public static RoleVO mergeRole(RoleVO currRole, RoleVO selfRole) {
        if (currRole == null) return selfRole;
        if (selfRole == null) return currRole;

        //菜单
        Set<String> menus = new HashSet<>();
        menus.addAll(currRole.getMenuIds());
        menus.addAll(selfRole.getMenuIds());
        currRole.setMenuIds(new ArrayList<>(menus));
        Map<String, List<String>> actions = currRole.getActions();
        Map<String, List<String>> selfActions = selfRole.getActions();
        if (actions == null) {
            currRole.setActions(selfActions);
        } else if (selfActions != null) {
            selfActions.forEach((k, v) -> {
                if (actions.containsKey(k)) {
                    if (v != null) {
                        Set<String> actionSet = new HashSet<>();
                        actionSet.addAll(v);
                        actionSet.addAll(actions.getOrDefault(k, new ArrayList<>()));
                        actions.put(k, new ArrayList<>(actionSet));
                    }
                } else {
                    actions.put(k, v);
                }
            });
        }
        //流程
        List<String> processIds1 = currRole.getProcessIds();
        List<String> processIds2 = selfRole.getProcessIds();
        Set<String> processIds = new HashSet<>();
        if (!CommonUtil.listIsBlank(processIds1))
            processIds.addAll(processIds1);
        if (!CommonUtil.listIsBlank(processIds2))
            processIds.addAll(processIds2);
        currRole.setProcessIds(new ArrayList<>(processIds));
        //对象
        Map<String, RoleVO.DataConfig> dataConfig = selfRole.getDataConfigs();
        Map<String, RoleVO.DataConfig> dataConfig1 = currRole.getDataConfigs();
        if (dataConfig != null && dataConfig.size() > 0) {
            if (dataConfig1 == null) {
                currRole.setDataConfigs(dataConfig);
            } else {
                for (String key : dataConfig.keySet()) {
                    RoleVO.DataConfig config = dataConfig.get(key);
                    if (!dataConfig1.containsKey(key)) {
                        dataConfig1.put(key, config);
                    } else {
                        RoleVO.DataConfig config1 = dataConfig1.get(key);
                        Set<String> notReadAttrs = new HashSet<>();
                        notReadAttrs.addAll(config1.getNotReadAttr() != null ? config1.getNotReadAttr() : new HashSet<>());
                        notReadAttrs.addAll(config.getNotReadAttr() != null ? config.getNotReadAttr() : new HashSet<>());
                        config1.setNotReadAttr(new ArrayList<>(notReadAttrs));

                        Set<String> notWriteAttrs = new HashSet<>();
                        notWriteAttrs.addAll(config1.getNotWriteAttr() != null ? config1.getNotWriteAttr() : new HashSet<>());
                        notWriteAttrs.addAll(config.getNotWriteAttr() != null ? config.getNotWriteAttr() : new HashSet<>());
                        config1.setNotWriteAttr(new ArrayList<>(notWriteAttrs));

                        List<ConditionsVO.Condition> conditions = new ArrayList<>();
                        conditions.addAll(config1.getConditions() != null ? config1.getConditions() : new ArrayList<>());
                        conditions.addAll(config.getConditions() != null ? config.getConditions() : new ArrayList<>());
                        config1.setConditions(conditions);
                    }
                }
            }
        }
        return currRole;
    }


    /**
     * 正太分布
     *
     * @param list
     * @return
     */
    public static List<Map> calculateNormalDistribution(List<Double> list) {
        //平均值
        double avg = list.stream().mapToDouble(Double::valueOf).average().orElse(0);
        //方差
        double var = Math.pow(list.stream().mapToDouble(item -> Math.pow(item - avg, 2)).sum() / list.size(), 0.5);
        //分布
        List<Map> featureNormal = new LinkedList<>();
        double normalBegin = avg - var / 2;
        double normalEnd = avg + var / 2;
        normalBegin = normalBegin - Math.abs(normalBegin * 0.2);
        normalEnd = normalEnd + Math.abs(normalEnd * 0.2);

        //均分100个区间
        Integer normalSize = 100;
        double normalWidth = (normalEnd - normalBegin) / normalSize;
        for (int i = 0; i < normalSize; i++) {
            double x = normalBegin + i * normalWidth;
            double y = (1 / (var * Math.sqrt(2 * Math.PI))) * Math.exp(-Math.pow((x - avg), 2) / (2 * Math.pow(var, 2)));
            Map<Object, Object> map = new HashMap<>();
            map.put("x", x);
            map.put("y", y);
            featureNormal.add(map);
        }
        return featureNormal;

    }


    /**
     * 从字符串里提取数字
     * @param str
     * @return
     */
    public static BigDecimal extractNumToDouble(String str){
        return new BigDecimal(extractNum(str));
    }

    public static String extractNum(String str){
        return Pattern.compile("[^0-9|.]").matcher(str).replaceAll("");
    }

    public static boolean isNumeric(Object str){
        try {
            new BigDecimal(str.toString());
            return true;
        }catch (Exception e){
            return false;
        }
    }
}
